#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <everest/odbc/Environment.h>
#include <everest/odbc/Connection.h>
#include <everest/odbc/SQLException.h>
#include <everest/odbc/Statement.h>
#include <everest/odbc/Recordset.h>
#include <stdio.h>
#include <iostream>

using namespace std;
using namespace NS_EVEREST;
using namespace NS_EVEREST::odbc;


class OdbcRecordsetTester : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE(OdbcRecordsetTester);
	CPPUNIT_TEST(test_open_recordset_with_statement);
	CPPUNIT_TEST(test_open_recordset_directly);
	CPPUNIT_TEST_SUITE_END();

public:
	OdbcRecordsetTester() {}
	~OdbcRecordsetTester() {}

	void setUp();
	void tearDown();

	void test_open_recordset_with_statement();
	void test_open_recordset_directly();

protected:
	EnvironmentPtr m_EnvPtr;
	ConnectionPtr  m_ConPtr;
};

CPPUNIT_TEST_SUITE_REGISTRATION(OdbcRecordsetTester);

void OdbcRecordsetTester::setUp()
{

	m_EnvPtr = new Environment();
	m_ConPtr = new Connection(m_EnvPtr.ref());

	try {
		m_ConPtr->open("DSN=EVEREST;UID=shi;PWD=shiyimin");
	} catch (const SQLException& e) {
		printf("SQL_Error: %d  %s  %s  %s\n", e.code(), e.state(), e.detail(), e.message());
		CPPUNIT_FAIL("Create connection failed");
	}
}

void OdbcRecordsetTester::tearDown()
{
	if ( m_ConPtr == NULL ) return;
	try {
		m_ConPtr->close();
		m_ConPtr.release();
	} catch (const SQLException& e) {
		printf("SQL_Error: %d  %s  %s  %s\n", e.code(), e.state(), e.detail(), e.message());
		m_ConPtr.release();
		CPPUNIT_FAIL("Close connection failed");
	}
}

void OdbcRecordsetTester::test_open_recordset_with_statement()
{
	printf("[info] begin test %s", "test_open_recordset_with_statement\n");
	String strsql = "SELECT id, name, dt, ts, height, size, area, image FROM test_param_1";

	StatementPtr stmtPtr;
	RecordsetPtr rsPtr;

	try {
		stmtPtr = new Statement(m_ConPtr.ref());
		int result = stmtPtr->prepare(strsql.c_str());
		CPPUNIT_ASSERT(EVEREST_ODBC_SUCCESS(result));

		rsPtr = new Recordset;

		result = rsPtr->open(stmtPtr.ref(), CURSOR_STATIC);
		CPPUNIT_ASSERT(EVEREST_ODBC_SUCCESS(result));


		int    count = 0;
		int    id = 0;
		String name;
		ByteVector image;
		DateTime dt;
		Timestamp ts;

		while ( rsPtr->moveNext() ) {
			count++;
			CPPUNIT_ASSERT(rsPtr->getInt32(1, id));
			CPPUNIT_ASSERT(rsPtr->getString(2, name));
			CPPUNIT_ASSERT(rsPtr->getDateTime(3, dt));
			CPPUNIT_ASSERT(rsPtr->getTimestamp(4, ts));
			CPPUNIT_ASSERT(rsPtr->getBinary(8, image));

			printf("row %d: id %d, name %s, dt %d-%d-%d %d:%d:%d, ts %ld.%06ld\n",
				   count, id, name.c_str(),
				   dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec,
				   ts.tv_sec, ts.tv_usec);
			for (size_t i = 0; i < image.size(); ++i) {
			//	if ( i % 16 == 0 ) printf("\n\t");
				printf("%02X ", image[i]);
			}
			printf("\n");
		}

		CPPUNIT_ASSERT(rsPtr->moveFirst());
		CPPUNIT_ASSERT(rsPtr->moveNext());
		CPPUNIT_ASSERT(rsPtr->moveLast());
		CPPUNIT_ASSERT(rsPtr->movePrior());
		CPPUNIT_ASSERT(rsPtr->move(-2, Recordset::MOVE_RELATIVE));
		CPPUNIT_ASSERT(rsPtr->move(2, Recordset::MOVE_ABSOLUTE));
	} catch (const SQLException& e) {
		printf("SQLException %s %s\n", e.source(), e.message());
	} catch (const Exception& e) {
		printf("Exception %s %s\n", e.source(), e.message());
	}

	rsPtr->close();
	stmtPtr->close();
}

void OdbcRecordsetTester::test_open_recordset_directly()
{
	printf("[info] begin test %s", "test_open_recordset_directly");
	String strsql = "SELECT id, name, dt, ts, height, size, area, image FROM test_param_1";

	RecordsetPtr rsPtr;

	try {
		rsPtr = new Recordset;

		int result = rsPtr->open(strsql.c_str(), m_ConPtr.ref(), CURSOR_STATIC);
		CPPUNIT_ASSERT(EVEREST_ODBC_SUCCESS(result));

		int    count = 0;
		int    id = 0;
		String name;
		ByteVector image;
		DateTime dt;
		Timestamp ts;

		while ( rsPtr->moveNext() ) {
			count++;
			CPPUNIT_ASSERT(rsPtr->getInt32(1, id));
			CPPUNIT_ASSERT(rsPtr->getString(2, name));
			CPPUNIT_ASSERT(rsPtr->getDateTime(3, dt));
			CPPUNIT_ASSERT(rsPtr->getTimestamp(4, ts));
			CPPUNIT_ASSERT(rsPtr->getBinary(8, image));

			printf("row %d: id %d, name %s, dt %d-%d-%d %d:%d:%d, ts %ld.%06ld\n",
				   count, id, name.c_str(),
				   dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec,
				   ts.tv_sec, ts.tv_usec);
			for (size_t i = 0; i < image.size(); ++i) {
			//	if ( i % 16 == 0 ) printf("\n\t");
				printf("%02X ", image[i]);
			}
			printf("\n");
		}

		CPPUNIT_ASSERT(rsPtr->moveFirst());
		CPPUNIT_ASSERT(rsPtr->moveNext());
		CPPUNIT_ASSERT(rsPtr->moveLast());
		CPPUNIT_ASSERT(rsPtr->movePrior());
		CPPUNIT_ASSERT(rsPtr->move(-2, Recordset::MOVE_RELATIVE));
		CPPUNIT_ASSERT(rsPtr->move(2, Recordset::MOVE_ABSOLUTE));
	} catch (const SQLException& e) {
		printf("SQLException %s %s\n", e.source(), e.message());
	} catch (const Exception& e) {
		printf("Exception %s %s\n", e.source(), e.message());
	}
}


int main(int argc, char **argv)
{
	CppUnit::TextUi::TestRunner runner;
	CppUnit::TestFactoryRegistry &reg = CppUnit::TestFactoryRegistry::getRegistry();
	runner.addTest(reg.makeTest());
	return !runner.run();
}
